﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Interactivity;
using System.Linq;
using System.Windows.Controls.Primitives;

namespace ScrumBoard
{
  public class ReorderBehavior : Behavior<Panel>
  {

    public FrameworkElement ScopeRoot
    {
      get { return (FrameworkElement)GetValue(ScopeRootProperty); }
      set { SetValue(ScopeRootProperty, value); }
    }
    public static readonly DependencyProperty ScopeRootProperty = DependencyProperty.Register("ScopeRoot", typeof(FrameworkElement), typeof(ReorderBehavior), null);

    public event EventHandler<ReorderingEventArgs> Reordering;
    public event EventHandler<ReorderingEventArgs> Reordered;


    protected override void OnAttached()
    {
      base.OnAttached();
      panel = AssociatedObject as Panel;
      if (panel != null)
      {
        panel.MouseLeftButtonDown += new MouseButtonEventHandler(panel_MouseLeftButtonDown);
      }
    }

    Panel panel;
    Popup dragIcon;
    Point startPosition;
    FrameworkElement draggingElem;
    DateTime lastSwitch;

    void panel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      startPosition = e.GetPosition(null);
      draggingElem = VisualTreeHelper.FindElementsInHostCoordinates(startPosition, panel).OfType<FrameworkElement>()
        .FirstOrDefault(el => el.VisualParent() == panel);
      if (draggingElem == null) return;
      draggingElem.CaptureMouse();
      draggingElem.MouseLeftButtonUp += new MouseButtonEventHandler(draggingElem_MouseLeftButtonUp);
      draggingElem.MouseMove += new MouseEventHandler(draggingElem_MouseMove);
    }

    void draggingElem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
      if (draggingElem == null) return;
      draggingElem.MouseLeftButtonUp -= draggingElem_MouseLeftButtonUp;
      draggingElem.MouseMove -= draggingElem_MouseMove;
      draggingElem.ReleaseMouseCapture();
      draggingElem = null;
      if (dragIcon != null)
      {
        dragIcon.IsOpen = false;
        dragIcon = null;
      }
    }

    private void draggingElem_MouseMove(object sender, MouseEventArgs e)
    {
      if (draggingElem == null) return;
      if ((DateTime.Now - lastSwitch).TotalMilliseconds < 100) return;

      Point position = e.GetPosition(null);
      if (dragIcon == null) createDragIcon();
      dragIcon.HorizontalOffset = position.X - dragIcon.Height / 2;
      dragIcon.VerticalOffset = position.Y - dragIcon.Width / 2;

      var newPanel = VisualTreeHelper.FindElementsInHostCoordinates(position, ScopeRoot ?? panel)
        .OfType<Panel>()
        .FirstOrDefault(p => Interaction.GetBehaviors(p).OfType<ReorderBehavior>().Count() > 0);

      if (newPanel == null) return;
      var oldPanel = (Panel)draggingElem.VisualParent();

      var hoverElem = VisualTreeHelper.FindElementsInHostCoordinates(position, newPanel).OfType<FrameworkElement>()
        .FirstOrDefault(el => el.VisualParent() == newPanel);

      if (newPanel == oldPanel)
      {
        if (hoverElem == null || hoverElem == draggingElem) return;
      }



      int newIndex = hoverElem == null ?
        newPanel.Children.Count()
        : newPanel.Children.IndexOf(hoverElem);

      if (Reordering != null)
      {
        var args = new ReorderingEventArgs(newPanel, draggingElem, newIndex);
        Reordering(this, args);
        if (args.Cancel) return;
      }

      oldPanel.Children.Remove(draggingElem);
      newPanel.Children.Insert(newIndex, draggingElem);
      lastSwitch = DateTime.Now;

      if (Reordered != null)
      {
        var args = new ReorderingEventArgs(newPanel, draggingElem, newIndex);
        Reordered(this, args);
      }
    }

    private void createDragIcon()
    {
      dragIcon = new Popup
      {
        Child = new Rectangle
        {
          StrokeThickness = 2,
          Stroke = new SolidColorBrush(Colors.Black),
          Height = draggingElem.ActualHeight,
          Width = draggingElem.ActualWidth,
          StrokeDashArray = { 10, 10 }
        },
        Height = draggingElem.ActualHeight,
        Width = draggingElem.ActualWidth,
        IsOpen = true
      };
    }

    protected override void OnDetaching()
    {
      if (panel != null)
      {
        panel.MouseLeftButtonDown -= panel_MouseLeftButtonDown; 
        panel = null;
        dragIcon = null;
        draggingElem = null;
      }
      base.OnDetaching();
    }
  }

  public class ReorderingEventArgs : EventArgs
  {
    public Panel NewPanel { get; private set; }
    public FrameworkElement DraggingElement { get; private set; }
    public int Index { get; private set; }
    public bool Cancel { get; set; }

    public ReorderingEventArgs(Panel newPanel, FrameworkElement draggingElement, int index)
    {
      NewPanel = newPanel;
      DraggingElement = draggingElement;
      Index = index;
    }
  }
}
